import {
  Component,
  Input,
  AfterContentInit,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  ContentChild,
  ChangeDetectorRef,
  TemplateRef,
  OnChanges,
  SimpleChanges,
  AfterContentChecked,
  QueryList,
  ContentChildren,
  ViewChild,
  NgZone,
} from '@angular/core';
import { NgControl, ControlContainer } from '@angular/forms';
import { startWith } from 'rxjs/operators';
import { CascaderComponent } from '../cascader/api';
import { TypeUtils } from '../core/api';
import { SelectComponent } from '../select/select.component';
import { HelpTooltipService } from '../core/services/help-tooltip.service';
@Component({
  selector: 'lv-form-control',
  templateUrl: './form-control.component.html',
  exportAs: 'lvFormControl',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    '[class.lv-form-control]': 'true',
  },
  providers: [HelpTooltipService],
})
export class FormControlComponent implements AfterContentInit, OnChanges, AfterContentChecked {
  // 帮助文字
  @Input() lvHelp: string | TemplateRef<void>;

  // 第一个formControl | ngModel | formControlName
  @ContentChild(NgControl, { static: false }) formCtrl;

  // error提示位置
  @Input() lvErrorTipPosition: 'bottom' | 'tooltip' = 'bottom';

  @ViewChild('helpTooltipTpl') helpTooltipTpl: TemplateRef<any>;

  // 第一个formGroup | formArrayName
  @ContentChild(ControlContainer, { static: false }) formCtrlContainer;

  // 错误提示类型 NEED_DO后续开放此接口
  @Input() lvErrorType: 'default' | 'multiple' | 'customer' = 'default';

  // 错误提示
  @Input() lvErrorTip: string | { [key: string]: string } | TemplateRef<{ $implicit: NgControl }>;

  @ContentChildren(SelectComponent, { descendants: true })
  _selectChildren: QueryList<SelectComponent>;

  @ContentChildren(CascaderComponent, { descendants: true })
  _cascaderChildren: QueryList<SelectComponent>;

  // 内置模板绑定的提示内容
  _errorTip: string;
  errKeys: string[];

  updateHelpToolTipTimer;

  // 是否合法
  _invalid: boolean = false;

  public typeUtils = TypeUtils;

  errorTips: Array<{ key: string; label: string }>;

  constructor(
    private cdr: ChangeDetectorRef,
    private helpTooltipService: HelpTooltipService,
    private zone: NgZone,
  ) {}

  // 暂用ContentChecked满足这种场景
  ngAfterContentChecked(): void {
    const flag = this.formCtrl?.invalid && !this.formCtrl.dirty && this.formCtrl.touched;
    if (flag) {
      this.formCtrlStatusChange();
    }
  }

  setLargeSize(): void {
    this._selectChildren.forEach((item): 'large' => (item.lvSize = 'large'));
    this._cascaderChildren.forEach((item): 'large' => (item.lvSize = 'large'));
  }

  // 当touched&!dirty时,statusChanges不会触发 https://github.com/angular/angular/issues/10887
  ngAfterContentInit(): void {
    // formGroup | formArrayName优先级高于formCtrl
    if (this.formCtrlContainer) {
      this.formCtrl = this.formCtrlContainer;
    }

    this.watchFormCtrl();
  }

  // 监听表单合法性
  watchFormCtrl(): void {
    // 当touched&&valid时,statusChanges不会触发 https://github.com/angular/angular/issues/10887
    if (this.formCtrl) {
      // tslint:disable-next-line:deprecation
      this.formCtrl.statusChanges.pipe(startWith(null)).subscribe((): void => {
        this.formCtrlStatusChange();
      });
    }
  }

  updateHelpToolTip(): void {
    this.helpTooltipService.status.next(
      this._invalid && this.lvErrorTipPosition === 'tooltip'
        ? {
            lvIcon: 'lv-icon-status-error',
            lvContent: this.lvErrorType === 'multiple' ? this.helpTooltipTpl : this._errorTip,
          }
        : {},
    );
  }

  // 表单状态响应
  formCtrlStatusChange(): void {
    this._invalid = this.formCtrl?.invalid && (this.formCtrl?.dirty || this.formCtrl?.touched);
    this.errKeys = [];
    if (this._invalid) {
      this.updateErrorTip();
    }
    this.zone.runOutsideAngular((): void => {
      if (this.updateHelpToolTipTimer) {
        clearTimeout(this.updateHelpToolTipTimer);
      }
      this.updateHelpToolTipTimer = setTimeout((): void => {
        this.updateHelpToolTip();
      });
    });
    this.cdr.markForCheck();
  }

  // 更新错误提示
  updateErrorTip(): void {
    if (this.lvErrorTip instanceof TemplateRef) {
      return;
    }

    // 若错误提示为对象, 则根据第一个错误的errKey关联错误提示
    if (typeof this.lvErrorTip === 'object') {
      const errors = this.formCtrl?.errors;
      if (errors) {
        this.errKeys = Object.keys(errors);
        const errKey = this.errKeys[0];
        // typeof null 也是'object', 取值前先判断是否存在
        this._errorTip = this.lvErrorTip?.[errKey] || errKey;
      } else {
        this._errorTip = null;
      }
    } else {
      // 若错误提示为字符串, 则所有错误类型共用提示
      this._errorTip = this.lvErrorTip;
    }
  }

  ngOnChanges(changes: SimpleChanges): void {
    const { lvErrorTip } = changes;

    if (lvErrorTip) {
      // Customer TemplateRef
      if (this.lvErrorTip instanceof TemplateRef) {
        this.lvErrorType = 'customer';
      } else {
        // 初始化时formCtrl undefined
        Promise.resolve().then((): void => {
          if (this.lvErrorType === 'multiple' && this.lvErrorTip instanceof Object) {
            this.errorTips = Object.keys(this.lvErrorTip).map((key): { label: any; key: string } => ({
              key,
              label: this.lvErrorTip[key],
            }));
          }
          this.updateErrorTip();
          this.cdr.markForCheck();
        });
      }
    }
  }
}
